#if !HC2020
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
#else
using GrxCAD.EditorInput;
using GrxCAD.Geometry;
#endif

using System;

namespace JoinBox
{
    public static partial class CadSystem
    {
        public static double Pi2 { get; } = Math.PI + Math.PI;

        /// <summary>
        /// 当前视口的观察方向
        /// </summary>
        /// <param name="ed"></param>
        /// <returns></returns>
        public static Vector3d GetViewport(this Editor ed)
        {
            var ret = ed.GetCurrentView().ViewDirection;//当前视口的观察方向
            return ret;
        }

        /// <summary>
        /// 获取坐标系统三维
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="wcs"></param>
        /// <param name="ucs"></param>
        public static void GetWcsUcs(this Editor ed, out CoordinateSystem3d wcs, out CoordinateSystem3d ucs)
        {
            Matrix3d Ide_wcs  = Matrix3d.Identity;//获取世界坐标系
            wcs               = Ide_wcs.CoordinateSystem3d;
            Matrix3d used_ucs = ed.CurrentUserCoordinateSystem;//当前用户坐标系
            ucs               = used_ucs.CoordinateSystem3d;
        }

        /// 原理见:http://www.cnblogs.com/graphics/archive/2012/08/10/2627458.html
        /// 以及:http://www.doc88.com/p-786491590188.html
        /// <summary>
        /// 输入一个向量(Z轴),获取它与世界坐标X轴和Y轴的角度,旋转这两个角度可以重合世界坐标的Z轴
        /// </summary>
        /// <param name="axis">一个坐标系的Z轴</param>
        /// <param name="XYZaxis">重合到哪个坐标系</param>
        public static void CoincideZAxis(this Vector3d axis, out double alx, out double aly)
        {
            //向量就是从0,0开始的 所以原点是重合的

            //世界坐标Z轴重合到用户坐标Z轴
            double ucs2zX = Math.Abs(axis.X) < 1e-10 ? 0 : axis.X;
            double ucs2zY = Math.Abs(axis.Y) < 1e-10 ? 0 : axis.Y;
            double ucs2zZ = Math.Abs(axis.Z) < 1e-10 ? 0 : axis.Z;

            //前两个旋转是为了世界z轴和用户z轴对齐,后一个旋转是为了对齐共面的XY平面.
            #region 求X旋转轴夹角
            Vector3d oq = Point3d.Origin.GetVectorTo(new Point3d(0, ucs2zY, ucs2zZ));
            alx = Vector3d.ZAxis.GetAngleTo(oq);//世界X轴是旋转轴
            alx = oq.Y > 0 ? alx : Pi2 - alx;
            alx = Math.Abs(Pi2 - alx) < 1e-10 ? 0 : alx;
            #endregion

            #region 求Y旋转轴夹角
            //原点到(用户Z轴的X,0,Z)向量与世界Z轴(0,0,1)的夹角,直接降为0不是XZ面的投影
            Vector3d or = Point3d.Origin.GetVectorTo(
              new Point3d(ucs2zX, 0,
                //根号下(Y平方+Z平方),在Y轴的旋转轴上,XZ面形成一个三角形,求出三角的玄
                //之所以要用根号下a平方+b平方，是因为如果直接设置了（x，0，z）就是画了圆弧到达xz面上，而不是投影到xz面上
                Math.Pow(Math.Pow(ucs2zY, 2) + Math.Pow(ucs2zZ, 2), 0.5)
              ));
            aly = Vector3d.ZAxis.GetAngleTo(or);
            aly = or.X < 0 ? aly : Pi2 - aly;
            aly = Math.Abs(Pi2 - aly) < 1e-10 ? 0 : aly;
            #endregion
        }

        /// <summary>
        /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大)
        /// </summary>
        /// <param name="ve">向量</param>
        /// <returns>X轴到向量的弧度</returns>
        public static double GetAngle2XAxis(this Vector2d ve)
        {
            double alz = Vector2d.XAxis.GetAngleTo(ve);//观察方向不要设置
            alz        = ve.Y > 0 ? alz : Pi2 - alz;   //逆时针为正,如果-负值控制正反
            alz        = Math.Abs(Pi2 - alz) < 1e-10 ? 0 : alz;
            return alz;
        }

        /// <summary>
        /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大)
        /// </summary>
        /// <param name="ve">向量</param>
        /// <returns>X轴到向量的弧度</returns>
        public static double GetAngle2XAxis(this Vector3d ve)
        {
            //世界重合到用户 Vector3d.XAxis->两点向量
            double alz = Vector3d.XAxis.GetAngleTo(ve);//观察方向不要设置
            alz        = ve.Y > 0 ? alz : Pi2 - alz;   //逆时针为正,如果-负值控制正反
            alz        = Math.Abs(Pi2 - alz) < 1e-10 ? 0 : alz;
            return alz;
        }

        /// <summary>
        /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大)
        /// </summary>
        /// <param name="startPoint">头点</param>
        /// <param name="endtPoint">尾点</param>
        /// <returns>X轴到向量的弧度</returns>
        public static double GetAngle2XAxis(Point2d startPoint, Point2d endtPoint)
        {
            Vector2d ve = startPoint.GetVectorTo(endtPoint);
            return ve.GetAngle2XAxis();
        }

        /// <summary>
        /// 通过两点获取与X轴的弧度,会超一个pi(上小,下大)
        /// </summary>
        /// <param name="startPoint">头点</param>
        /// <param name="endtPoint">尾点</param>
        /// <returns>X轴到向量的弧度</returns>
        public static double GetAngle2XAxis(this Point3d startPoint, Point3d endtPoint)
        {
            //防止向量z轴有值造成检测角度不是一个平面
            Point3d pt1 = new Point3d(startPoint.X, startPoint.Y, 0);
            Point3d pt2 = new Point3d(endtPoint.X, endtPoint.Y, 0);
            //世界重合到用户 Vector3d.XAxis->两点向量
            return pt1.GetVectorTo(pt2).GetAngle2XAxis();
        }

        /// <summary>
        /// 三个轴角度
        /// 重合两个坐标系的Z轴,求出三个轴旋转角度,后续利用旋转角度正负和次序来变换用户和世界坐标系
        /// </summary>
        /// <param name="ucs2">用户坐标系</param>
        /// <param name="alx">坐标系间的X轴旋转角度</param>
        /// <param name="aly">坐标系间的Y轴旋转角度</param>
        /// <param name="alz">坐标系间的Z轴旋转角度</param>
        public static void ToWcsAxisAngles(this CoordinateSystem3d ucs, out double alx, out double aly, out double alz)
        {
            //ucs可能带有原点,设置到0,世界坐标系原点重合
            var ucs2 = new CoordinateSystem3d(Point3d.Origin, ucs.Xaxis, ucs.Yaxis);//XY轴通过叉乘求得Z轴
            ucs2.Zaxis.CoincideZAxis(out alx, out aly);
            //两个X轴的角度对比求出Z轴旋转角,所以旋转X轴,使其与世界X轴的XY面共面.
            Vector3d newXa = ucs2.Xaxis.RotateBy(alx, Vector3d.XAxis).RotateBy(aly, Vector3d.YAxis);
            alz = -newXa.GetAngle2XAxis();
        }

        /// <summary>
        /// 获取三个轴角度
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="act"></param>
        //public static void GetAxisAngles(this Editor ed, Action<double, double, double> act)
        //{
        //    //当前用户坐标
        //    ed.GetWcsUcs(out CoordinateSystem3d wcs, out CoordinateSystem3d ucs);
        //    ucs.ToWcsAxisAngles(out double alx, out double aly, out double alz);
        //    act(alx, aly, alz);
        //}


        private static Point3d WcsToUcs(this Point3d pta, CoordinateSystem3d ucs)
        {
            ucs.ToWcsAxisAngles(out double alx, out double aly, out double alz);

            //平移
            double ptX = pta.X - ucs.Origin.X;
            double ptY = pta.Y - ucs.Origin.Y;
            double ptZ = pta.Z - ucs.Origin.Z;
            Point3d pt = new Point3d(ptX, ptY, ptZ);
            pt = pt.RotateBy(alx, Vector3d.XAxis, Point3d.Origin).
                    RotateBy(aly, Vector3d.YAxis, Point3d.Origin).
                    RotateBy(alz, Vector3d.ZAxis, Point3d.Origin);
            return pt;
        }

        private static Point3d UcsToWcs(this Point3d pta, CoordinateSystem3d ucs)
        {
            ucs.ToWcsAxisAngles(out double alx, out double aly, out double alz);

            Point3d pt = pta.RotateBy(-alz, Vector3d.ZAxis, Point3d.Origin).
                 RotateBy(-aly, Vector3d.YAxis, Point3d.Origin).
                 RotateBy(-alx, Vector3d.XAxis, Point3d.Origin);

            //平移
            double ptX = pt.X + ucs.Origin.X;
            double ptY = pt.Y + ucs.Origin.Y;
            double ptZ = pt.Z + ucs.Origin.Z;

            pt = new Point3d(ptX, ptY, ptZ);//世界坐标系的点位置
            return pt;
        }

        /// <summary>
        /// 把一个点从一个坐标系统变换到另外一个坐标系统
        /// </summary>
        /// <param name="userPt">来源点</param>
        /// <param name="source">来源坐标系</param>
        /// <param name="target">目标坐标系</param>
        /// <returns>目标坐标系的点</returns>
        public static Point3d Transform(this Point3d userPt, CoordinateSystem3d source, CoordinateSystem3d target)
        {
            //世界坐标是独特的
            CoordinateSystem3d wcs = Matrix3d.Identity.CoordinateSystem3d;
            Point3d pt = Point3d.Origin;
            if (Equals(source, target))
            {
                pt = userPt;
            }
            //al的角度是一样的,旋转方式取决于正负号
            else if (!Equals(source, wcs) && !Equals(target, wcs))//用户转用户
            {
                pt = userPt.UcsToWcs(source).WcsToUcs(target);
            }
            if (Equals(target, wcs))
            {
                pt = userPt.UcsToWcs(source);
            }
            else if (!Equals(target, wcs))
            {
                pt = userPt.WcsToUcs(target);
            }
            return pt;
        }
    }
}